package com.xsd.parser.util;

import com.xsd.parser.bean.xs.*;
import com.xsd.parser.model.ElementListModel;
import com.xsd.parser.module.AnnotationModel;
import com.xsd.parser.module.ImportModel;
import com.xsd.parser.module.SchemaModel;
import com.xsd.parser.parse.ExcelParseToCollection;
import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class XsdParserUtil {

    private ExcelParseToCollection excelParseToCollection;
    private static AtomicInteger elementCount = new AtomicInteger(0);
    private static AtomicInteger attributeCount = new AtomicInteger(0);
    private static AtomicInteger simpleCount = new AtomicInteger(0);
    private static AtomicInteger complexCount = new AtomicInteger(0);
    private Schema schema;

    private List<String> elementNameList;
    private List<String> attributeNameList;

    private String prefix = "";
    public XsdParserUtil(ExcelParseToCollection excelParseToCollection) {
        this.excelParseToCollection = excelParseToCollection;
        schema = new Schema();
        elementNameList = new ArrayList<>();
        attributeNameList = new ArrayList<>();
    }

    /**
     * 转换 schema
     * @return schema
     */
    public  Schema parserSchema() throws Exception {
        List<SchemaModel> schemaModels =  excelParseToCollection.schemaParse();
        List<ImportModel> importModels = excelParseToCollection.importParse();
        List<AnnotationModel> annotationModels = excelParseToCollection.annotationParse();


        for (SchemaModel schemaModel : schemaModels) {
            if("targetNamespace".equals(schemaModel.getSchemaName())) {
                schema.setTargetNamespace(schemaModel.getSchemaValue());
            }
            if ("appinfo:defaultPrefix".equals(schemaModel.getSchemaName())) {
                prefix = schemaModel.getSchemaValue();
                schema.setDefaultPrefix(schemaModel.getSchemaValue());
            }
            if ("version".equals(schemaModel.getSchemaName())) {
                schema.setVersion(schemaModel.getSchemaValue());
            }
            if ("attributeFormDefault".equals(schemaModel.getSchemaName())) {
                schema.setAttributeFormDefault(FormChoice.QUALIFIED);
            }
            if ("elementFormDefault".equals(schemaModel.getSchemaName())) {
                schema.setElementFormDefault(FormChoice.QUALIFIED);
            }
            if ("blockDefault".equals(schemaModel.getSchemaName())) {
                schema.getBlockDefault().add("");
            }
            if ("finalDefault".equals(schemaModel.getSchemaName())) {
                schema.getFinalDefault().add("");
            }
            if ("xml:lang".equals(schemaModel.getSchemaName())) {
                schema.setLang("");
            }

            boolean setBool = !"xmlns:xs".equals(schemaModel.getSchemaName())
                        && !"targetNamespace".equals(schemaModel.getSchemaName())
                        && !"version".equals(schemaModel.getSchemaName())
                        && !"attributeFormDefault".equals(schemaModel.getSchemaName())
                        && !"elementFormDefault".equals(schemaModel.getSchemaName())
                        && !"blockDefault".equals(schemaModel.getSchemaName())
                        && !"finalDefault".equals(schemaModel.getSchemaName())
                        && !"xml:lang".equals(schemaModel.getSchemaName())
                        && !"appinfo:defaultPrefix".equals(schemaModel.getSchemaName());
            if (setBool){
                schema.getOtherAttributes().put(QName.valueOf(schemaModel.getSchemaName()),schemaModel.getSchemaValue());
            }
        }

        for (ImportModel importModel : importModels) {
            schema.getIncludeOrImportOrRedefine().add(parserImport(importModel));
        }
        for (AnnotationModel annotationModel :annotationModels) {
            schema.getIncludeOrImportOrRedefine().add(parserAnnotation(annotationModel));
        }
        parserElementAll();
        return schema;
    }

    private void parserElementAll() throws Exception {
        List<ElementListModel> elementModels = excelParseToCollection.elementParse();
        for (ElementListModel elementModel : elementModels) {
            if ("xs:complexType".equalsIgnoreCase(elementModel.getElementType().trim())) {
                schema.getSimpleTypeOrComplexTypeOrGroup().add(parserTopLevelComplexType(elementModel));
            } else if ("xs:simpleType".equalsIgnoreCase(elementModel.getElementType().trim())) {
                schema.getSimpleTypeOrComplexTypeOrGroup().add(parserTopLevelSimpleType(elementModel));
            } else if ("自由组件".equals(elementModel.getElementType().trim())) {
                if ("xs:element".equalsIgnoreCase(elementModel.getElementType())) {
                    if (!elementNameList.contains(elementModel.getDataElementEN()) && prefix.equals(elementModel.getDataElementEN().split(":")[0])) {
                        schema.getSimpleTypeOrComplexTypeOrGroup().add(parserTopLevelElement(elementModel));
                        elementNameList.add(elementModel.getDataElementEN());
                    }
                }
                if ("xs:attribute".equalsIgnoreCase(elementModel.getElementType())) {
                    if (!attributeNameList.contains(elementModel.getDataElementEN()) && prefix.equals(elementModel.getDataElementEN().split(":")[0])) {
                        schema.getSimpleTypeOrComplexTypeOrGroup().add(parserAttribute(elementModel));
                        attributeNameList.add(elementModel.getDataElementEN());
                    }
                }
            }
        }
    }
    /**
     * Import
     * @return Import
     */
    private Import parserImport(ImportModel importModel){
        Import ipt = new Import();
        ipt.setNamespace(importModel.getNamespace());
        ipt.setSchemaLocation(importModel.getSchemaLocation());
        if ("true".equals(importModel.getExternalImportIndicator()) ){
            ipt.getOtherAttributes().put(QName.valueOf("appinfo:externalImportIndicator"),"true");
            ipt.setAnnotation(parserAnnotation(newInstance(importModel.getImportDefCN(),importModel.getImportDefEN())));
        }
        return ipt;
    }

    /**
     * 转换 Annotation
     * @return Annotation
     */
    private Annotation parserAnnotation(AnnotationModel annotationModel){
        Annotation annotation = new Annotation();
        annotation.getAppinfoOrDocumentation().add(parserDocumentation("CN",annotationModel.getAnnotationCN()));
        annotation.getAppinfoOrDocumentation().add(parserDocumentation("EN",annotationModel.getAnnotationEN()));
        return  annotation;
    }

    /**
     * 转换 Documentation
     * @return Documentation
     */
    private Documentation parserDocumentation(String language ,String content){
        Documentation documentation = new Documentation();
        documentation.setLang(language);
        documentation.getContent().add(content);
        return  documentation;
    }

    /**
     * 转换 Element
     * @return TopLevelElement
     */
    private Element parserTopLevelElement(ElementListModel elementModel) {
        Element element = new TopLevelElement();
        element.setId(newIdentity(elementModel,"EM",elementCount.incrementAndGet()));
        if ("Abstract".equalsIgnoreCase(elementModel.getDataBaseTypeEN())){
            element.setAbstract(true);
        }else {
            element.setType(elementModel.getDataBaseTypeEN());
        }
        if (elementModel.getSubstitutionGroup() != null && !"".equals(elementModel.getSubstitutionGroup())){
            element.setSubstitutionGroup(elementModel.getSubstitutionGroup());
        }
        AnnotationModel annotationModel =newInstance(elementModel.getDataModuleDefCN() + (elementModel.getAllRefStandard() != null && !"".equals(elementModel.getAllRefStandard().trim())? "见" + elementModel.getAllRefStandard() : "" ),elementModel.getDataModuleDefEN());
        element.setAnnotation(parserAnnotation(annotationModel));
        element.setName(localName(elementModel.getDataElementEN()));
        element.setCNName(elementModel.getDataModuleCN());
        element.setNillable(true);
        element.getBlock().add("restriction");
        element.getBlock().add("extension");
        element.getFinal().add("#all");
        return element;
    }

    /**
     * 转换 Attribute
     * @return Attribute
     */
    private Attribute parserAttribute(ElementListModel elementModel){
        Attribute attribute = new TopLevelAttribute();
        attribute.setId(newIdentity(elementModel,"AB",attributeCount.incrementAndGet()));
        attribute.setType(elementModel.getDataBaseTypeEN());
        attribute.setName(localName(elementModel.getDataElementEN()));
        attribute.setCNName(elementModel.getDataModuleCN());
        AnnotationModel annotation = newInstance(elementModel.getDataModuleDefCN() + (elementModel.getAllRefStandard() != null && !"".equals(elementModel.getAllRefStandard().trim())? "见" + elementModel.getAllRefStandard() : "" ),elementModel.getDataModuleDefEN());
        attribute.setAnnotation(parserAnnotation(annotation));
        return attribute;
    }
    /**
     * 转换 simpleTye
     * @return  simpleTye
     */
    private TopLevelSimpleType parserTopLevelSimpleType(ElementListModel elementModel){
        TopLevelSimpleType simpleType = new TopLevelSimpleType();
        simpleType.setId(newIdentity(elementModel,"ST",simpleCount.incrementAndGet()));
        simpleType.setName(localName(elementModel.getDataTypeEN()));
        simpleType.getFinal().add("#all");
        simpleType.setCNName(elementModel.getDataModuleCN());
        AnnotationModel annotationModel =newInstance(elementModel.getDataModuleDefCN() + (elementModel.getAllRefStandard() != null && !"".equals(elementModel.getAllRefStandard().trim())? "见" + elementModel.getAllRefStandard() : "" ),elementModel.getDataModuleDefEN());
        simpleType.setAnnotation(parserAnnotation(annotationModel));
        simpleType.setRestriction(parserRestriction(elementModel.getDataBaseTypeEN(),elementModel.getElementList()));
        return simpleType;
    }

    /**
     * 获取名称
     * @param name
     * @return 返回正常名称
     */
    private String localName(String name){
        return name!=null && name.contains(":") ?name.split(":")[1]: name;
    }
    /**
     * 转换 Restriction
     * @return Restriction
     */
    private Restriction parserRestriction(String baseName,List<ElementListModel> elementListModels){
        Restriction restriction = new Restriction();
        restriction.setBase(baseName);

        for (ElementListModel elementListModel :elementListModels) {
            if ("xs:enumeration".equalsIgnoreCase(elementListModel.getElementType())){
                restriction.getFacets().add(createEnumeration(elementListModel));
            }else if ("xs:minInclusive".equalsIgnoreCase(elementListModel.getElementType())){
                restriction.getFacets().add(createMinInclusive(elementListModel));
            }else if ("xs:maxInclusive".equalsIgnoreCase(elementListModel.getElementType())){
                restriction.getFacets().add(createMaxInclusive(elementListModel));
            }else if ("xs:minExclusive".equalsIgnoreCase(elementListModel.getElementType())){
                restriction.getFacets().add(createMinExclusive(elementListModel));
            }else if ("xs:maxExclusive".equalsIgnoreCase(elementListModel.getElementType())){
                restriction.getFacets().add(createMaxExclusive(elementListModel));
            }
        }
        return restriction;
    }
    /**
     * 转换 Restriction
     * @return Restriction
     */
    private JAXBElement<NoFixedFacet> createEnumeration(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        NoFixedFacet facet = new NoFixedFacet();
        facet.setValue(elementListModel.getDataElementEN());
        AnnotationModel annotation = newInstance(elementListModel.getDataModuleDefCN() + (elementListModel.getAllRefStandard() != null && !"".equals(elementListModel.getAllRefStandard().trim())? "见" + elementListModel.getAllRefStandard() : "" ),elementListModel.getDataModuleDefEN());
        facet.setAnnotation(parserAnnotation(annotation));
        return objectFactory.createEnumeration(facet);
    }

    /**
     * 转换 Restriction
     * @return Restriction
     */
    private JAXBElement<Facet> createMinInclusive(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Facet facet = new Facet();
        facet.setValue(elementListModel.getDataElementEN());
        return objectFactory.createMinInclusive(facet);
    }
    /**
     * 转换 Restriction
     * @return Restriction
     */
    private JAXBElement<Facet> createMaxInclusive(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Facet facet = new Facet();
        facet.setValue(elementListModel.getDataElementEN());
        return objectFactory.createMaxInclusive(facet);
    }

    /**
     * 转换 Restriction
     * @return Restriction
     */
    private JAXBElement<Facet> createMinExclusive(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Facet facet = new Facet();
        facet.setValue(elementListModel.getDataElementEN());
        return objectFactory.createMinExclusive(facet);
    }

    /**
     * 转换 Restriction
     * @return Restriction
     */
    private JAXBElement<Facet> createMaxExclusive(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Facet facet = new Facet();
        facet.setValue(elementListModel.getDataElementEN());
        return objectFactory.createMaxExclusive(facet);
    }

    /**
     * 转化 ComplexType
     * @return ComplexType
     */
    private ComplexType parserTopLevelComplexType(ElementListModel elementModel){
        ComplexType complexType = new TopLevelComplexType();
        complexType.setId(newIdentity(elementModel,"CT",complexCount.incrementAndGet()));
        complexType.setName(localName(elementModel.getDataTypeEN()));
        complexType.setCNName(elementModel.getDataModuleCN());
        complexType.getBlock().add("#all");
        complexType.getFinal().add("restriction");
        complexType.setMixed(false);
        complexType.setAbstract(false);
        AnnotationModel annotationModel = newInstance(elementModel.getDataModuleDefCN() + (elementModel.getAllRefStandard() != null && !"".equals(elementModel.getAllRefStandard().trim())? "见" + elementModel.getAllRefStandard() : "" ),elementModel.getDataModuleDefEN());
        complexType.setAnnotation(parserAnnotation(annotationModel));
        if (elementModel.getElementList() != null && elementModel.getElementList().size() > 0){
            List<ElementListModel> subElementListModel = elementModel.getElementList();
            long count = subElementListModel.stream().filter(ele -> ele.getElementType()!= null && "xs:element".equalsIgnoreCase(ele.getElementType())).count();
            if (count > 0){
                complexType.setComplexContent(parserComplexContent(elementModel.getDataBaseTypeEN(),subElementListModel));
            }else{
                complexType.setSimpleContent(parserSimpleContent(elementModel.getDataBaseTypeEN(),subElementListModel));
            }
        }

        return complexType;
    }

    /**
     * 转化 ComplexContent
     * @return ComplexContent
     */
    private ComplexContent parserComplexContent(String baseQName,List<ElementListModel> subElementListModel){
        ComplexContent complexContent = new ComplexContent();
        complexContent.setMixed(false);
        complexContent.setExtension(parserExtensionType(baseQName,subElementListModel));
        return complexContent;
    }

    /**
     * 转化 SimpleContent
     * @return SimpleContent
     */
    private SimpleContent parserSimpleContent(String baseQName,List<ElementListModel> subElementListModel){
        SimpleContent simpleContent = new SimpleContent();
        simpleContent.setExtension(parserSimpleExtensionType(baseQName,subElementListModel));
        return simpleContent;
    }

    /**
     * 转化 SimpleExtensionType
     * @return SimpleExtensionType
     */
    private SimpleExtensionType parserSimpleExtensionType(String baseQName,List<ElementListModel> subElementListModel){
        SimpleExtensionType simpleExtensionType = new SimpleExtensionType();
        simpleExtensionType.setBase(baseQName);
        if (subElementListModel != null && subElementListModel.size() > 0 ){
            Map<String,List<ElementListModel>> ma = subElementListModel.stream().collect(Collectors.groupingBy(ElementListModel::getElementType));
            ma.forEach((key,value) ->{
                if ("xs:attribute".equals(key)){
                    for (ElementListModel elementListModel :value) {
                        simpleExtensionType.getAttributeOrAttributeGroup().add(parRefAttribute(elementListModel));
                        if (!attributeNameList.contains(elementListModel.getDataElementEN()) && prefix.equals(elementListModel.getDataElementEN().split(":")[0])) {
                            schema.getSimpleTypeOrComplexTypeOrGroup().add(parserAttribute(elementListModel));
                            attributeNameList.add(elementListModel.getDataElementEN());
                        }

                    }
                }else  if("xs:attributeGroup".equalsIgnoreCase(key)){
                    for (ElementListModel elementListModel :value) {
                        simpleExtensionType.getAttributeOrAttributeGroup().add(createAttributeGroup(elementListModel));
                    }
                } else{
                    simpleExtensionType.setSequence(parserExplicitGroup(subElementListModel));
                }

            });
        }

        return simpleExtensionType;
    }

    /**
     * 转化 ExtensionType
     * @return ExtensionType
     */
    private ExtensionType parserExtensionType(String baseQName,List<ElementListModel> subElementListModel){
        ExtensionType extensionType = new ExtensionType();
        extensionType.setBase(baseQName);
        if (subElementListModel != null && subElementListModel.size() > 0){
            Map<String,List<ElementListModel>> map = subElementListModel.stream().collect(Collectors.groupingBy(ElementListModel::getElementType));
            map.forEach((key,value) ->{
                if ("xs:element".equalsIgnoreCase(key)){
                    extensionType.setSequence(parserExplicitGroup(value));
                }
                if ("xs:attribute".equalsIgnoreCase(key)){
                    for (ElementListModel e :value ) {
                        extensionType.getAttributeOrAttributeGroup().add(createAttributeRef(e));
                        if ("xs:attribute".equalsIgnoreCase(e.getElementType())) {
                            if (!attributeNameList.contains(e.getDataElementEN()) && prefix.equals(e.getDataElementEN().split(":")[0])) {
                                schema.getSimpleTypeOrComplexTypeOrGroup().add(parserAttribute(e));
                                attributeNameList.add(e.getDataElementEN());
                            }
                        }
                    }
                }
            });

        }


        return extensionType;
    }

    /**
     * 引用的Attribute 制作
     * @param elementListModel
     * @return Attribute
     */
    private Attribute parRefAttribute(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Attribute attribute = objectFactory.createAttribute();
        attribute.setRef(elementListModel.getDataElementEN());
        attribute.setUse("optional");
        if(!attributeNameList.contains(elementListModel.getDataElementEN()) && prefix.equals(elementListModel.getDataElementEN().split(":")[0])){
            schema.getSimpleTypeOrComplexTypeOrGroup().add(parserAttribute(elementListModel));
            attributeNameList.add(elementListModel.getDataElementEN());
        }
        return attribute;
    }
    /**
     * 转化 AttributeGroupRef
     * @param subElementListModel
     * @return AttributeGroupRef
     */
    private Annotated createAttributeGroup(ElementListModel subElementListModel){
        AttributeGroupRef attributeGroupRef = new AttributeGroupRef();
        attributeGroupRef.setRef(subElementListModel.getDataElementEN());
        return attributeGroupRef;
    }
    /**
     * 转化 ComplexRestrictionType
     * @return ComplexRestrictionType
     */
    private ComplexRestrictionType parserComplexRestrictionType(){
        ComplexRestrictionType complexRestrictionType = new ComplexRestrictionType();
        complexRestrictionType.setBase("cc:dcba");
        return complexRestrictionType;
    }

    /**
     * 转换 ExplicitGroup
     * @return ExplicitGroup
     */
    private ExplicitGroup parserExplicitGroup(List<ElementListModel> subElementListModel){
        ExplicitGroup explicitGroup = new ExplicitGroup();
        explicitGroup.setMinOccurs(BigInteger.ONE);
        explicitGroup.setMaxOccurs("1");
        if (subElementListModel !=null && subElementListModel.size() > 0) {
            for (ElementListModel elementListModel:subElementListModel) {
                if ("xs:element".equalsIgnoreCase(elementListModel.getElementType())){
                    explicitGroup.getParticle().add(createGroupElement(elementListModel));
                    if(!elementNameList.contains(elementListModel.getDataElementEN()) && prefix.equals(elementListModel.getDataElementEN().split(":")[0])){
                        schema.getSimpleTypeOrComplexTypeOrGroup().add(parserTopLevelElement(elementListModel));
                        elementNameList.add(elementListModel.getDataElementEN());
                    }
                }
            }
        }
        return explicitGroup;
    }

    /**
     * 转换 LocalElement
     * @return LocalElement
     */
    private JAXBElement<LocalElement> createGroupElement(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        LocalElement localElement = objectFactory.createLocalElement();
        localElement.setRef(elementListModel.getDataElementEN());
        localElement.setMinOccurs(BigInteger.ZERO);
        localElement.setMaxOccurs("unbounded");
        return objectFactory.createGroupElement(localElement);
    }

    /**
     * 转换 Attribute（TopLevelAttribute） ref 用
     * @return TopLevelAttribute
     */
    private Attribute createAttributeRef(ElementListModel elementListModel){
        ObjectFactory objectFactory = new ObjectFactory();
        Attribute attribute = objectFactory.createAttribute();
        attribute.setRef(elementListModel.getDataElementEN());
        attribute.setUse("optional");
        return attribute;
    }

    private AnnotationModel newInstance(String CNName,String ENName){
        AnnotationModel annotationModel = new AnnotationModel();
        annotationModel.setAnnotationCN(CNName);
        annotationModel.setAnnotationEN(ENName);
        return annotationModel;
    }

    private String newIdentity(ElementListModel elementListModel ,String prefix,Integer num){
        return prefix + String.format("%06d", num) + "_" + (elementListModel.getVersion() == null || "".equals(elementListModel.getVersion().trim()) ? "1.0" : elementListModel.getVersion()) ;
    }
}
